/*
 * Lobby.java
 *
 * Created on 29 de Julho de 2007, 16:47
 */

package org.gpp.proj1.ui.gui.lobby;

import java.awt.event.ActionEvent;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.ListModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;

import org.gpp.proj1.communication.CommunicationException;
import org.gpp.proj1.communication.listener.OfflineClientListener;
import org.gpp.proj1.logic.core.server.ServerPhaseException;
import org.gpp.proj1.ui.gui.SRGGUIView;

/**
 *
 * @author  Gustavo
 */
public class SCRLobby extends javax.swing.JFrame implements OfflineClientListener {
    
    private static final long serialVersionUID = -7146011687806202291L;
    
    private static final String MAPS_ROOT = "./res/map/";
    
    private static final String MAP_FILE_EXTENSION = ".map";
    
    private File[] mapFiles;
    
    private String[] mapNames;
    
	private boolean server;
	
	private Map<Integer, String> rowClientMapping;
		
    private Map<String, String> aliasMap;
	
    private Set<String> readyClientsSet;

	private final SRGGUIView view;
	
	private boolean isReady;
	
    private javax.swing.JButton backButton;
    private javax.swing.JSeparator bottomSeparator;
    private javax.swing.JButton changeMapButton;
    private javax.swing.JScrollPane chatScrollPane;
    private javax.swing.JTextArea chatTextArea;
    private javax.swing.JButton disconnectButton;
    private javax.swing.JButton startButton;
    private javax.swing.JLabel instructionLabel;
    private javax.swing.JButton kickButton;
    private javax.swing.JLabel mapLabel;
    private javax.swing.JList mapList;
    private javax.swing.JLabel mapTextLabel;
    private javax.swing.JLabel messageLabel;
    private javax.swing.JTextField messageTextField;
    private javax.swing.JLabel serverNameLabel;
    private javax.swing.JLabel nameTextLabel;
    private javax.swing.JScrollPane playersScrollPane;
    private javax.swing.JTable playersTable = new JTable();
    private javax.swing.JToggleButton readyToggleButton;
    private javax.swing.JButton sendButton;

	private String selectedPlayer;
    
    /** Creates new form Lobby */
    public SCRLobby( SRGGUIView view, boolean server ) {
    	this.rowClientMapping = new HashMap<Integer, String>();
    	
        this.view = view;
        this.view.renewLobby( this );
        
		this.loadMaps();
        initComponents();
        this.setLocationRelativeTo(null);
        
        this.server = server;
        
        if ( this.isServer() ) {
            this.mapList.setEnabled( true );
            if (this.mapList.getModel().getSize() > 0 ) {
                this.mapList.setSelectedIndex( 0 );
            }
        }
        
        this.isReady = false;
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        instructionLabel = new javax.swing.JLabel();
        serverNameLabel = new javax.swing.JLabel();
        nameTextLabel = new javax.swing.JLabel();
        mapLabel = new javax.swing.JLabel();
        mapList = new javax.swing.JList();
        changeMapButton = new javax.swing.JButton();
        kickButton = new javax.swing.JButton();
        messageLabel = new javax.swing.JLabel();
        messageTextField = new javax.swing.JTextField();
        sendButton = new javax.swing.JButton();
        backButton = new javax.swing.JButton();
        startButton = new javax.swing.JButton();
        readyToggleButton = new javax.swing.JToggleButton();
        playersScrollPane = new javax.swing.JScrollPane();
        mapTextLabel = new javax.swing.JLabel();
        disconnectButton = new javax.swing.JButton();
        chatScrollPane = new javax.swing.JScrollPane();
        chatTextArea = new javax.swing.JTextArea();
        bottomSeparator = new javax.swing.JSeparator();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle( "SomeRobotGame - " + view.getController().getCurrentProfile().getName() );
        setResizable(false);
        instructionLabel.setText("Game lobby");

        serverNameLabel.setText("Server name:");

        mapLabel.setText("Map:");

        mapList.setModel(getMapsListModel());
        mapList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        mapList.setEnabled(false);
        mapList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                mapListValueChanged(evt);
            }
        });
        
        playersTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        playersTable.getSelectionModel().addListSelectionListener(new javax.swing.event.ListSelectionListener() {
            public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
                playerTableValueChanged(evt);
            }
        });

        changeMapButton.setText("Change");
        changeMapButton.setEnabled(false);
        changeMapButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                changeMapButtonActionPerformed(evt);
            }
        });

        kickButton.setText("Kick");
        kickButton.setEnabled(false);
        kickButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                kickButtonActionPerformed(evt);
            }
        });


        messageLabel.setText("Message:");

        sendButton.setText("Send");
        sendButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sendButtonActionPerformed(evt);
            }
        });

        backButton.setText("Back");
        backButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                backButtonActionPerformed(evt);
            }
        });

        startButton.setText("Start game");
        startButton.setEnabled(false);
        startButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startGameActionPerformed(evt);
            }
        });      

        readyToggleButton.setText("Ready");
        readyToggleButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                readyButtonActionPerformed(evt);
            }
        });
      
        playersScrollPane.setViewportView(playersTable);

        mapTextLabel.setText(" ");

        disconnectButton.setText("Disconnect");
        disconnectButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                disconnectButtonActionPerformed(evt);
            }
        });

        chatTextArea.setLineWrap(true);
        chatTextArea.setBorder(null);
        chatScrollPane.setViewportView(chatTextArea);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(layout.createSequentialGroup()
                        .add(bottomSeparator, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 415, Short.MAX_VALUE)
                        .addContainerGap())
                    .add(layout.createSequentialGroup()
                        .add(chatScrollPane, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 415, Short.MAX_VALUE)
                        .addContainerGap())
                    .add(instructionLabel)
                    .add(layout.createSequentialGroup()
                        .add(serverNameLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(nameTextLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 344, Short.MAX_VALUE)
                        .addContainerGap())
                    .add(layout.createSequentialGroup()
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(changeMapButton)
                            .add(mapList, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 141, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 22, Short.MAX_VALUE)
                        .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                            .add(kickButton)
                            .add(playersScrollPane, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 252, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addContainerGap())
                    .add(layout.createSequentialGroup()
                        .add(backButton)
                        .add(40, 40, 40)
                        .add(disconnectButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(startButton)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 63, Short.MAX_VALUE)
                        .add(readyToggleButton)
                        .addContainerGap())
                    .add(layout.createSequentialGroup()
                        .add(messageLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(messageTextField, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 302, Short.MAX_VALUE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(sendButton)
                        .addContainerGap())
                    .add(layout.createSequentialGroup()
                        .add(mapLabel)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(mapTextLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 385, Short.MAX_VALUE)
                        .addContainerGap())))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(instructionLabel)
                .add(20, 20, 20)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(serverNameLabel)
                    .add(nameTextLabel))
                .add(15, 15, 15)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(mapLabel)
                    .add(mapTextLabel))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
                    .add(playersScrollPane, 0, 0, Short.MAX_VALUE)
                    .add(mapList, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(kickButton)
                    .add(changeMapButton))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(chatScrollPane, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 129, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(messageLabel)
                    .add(messageTextField, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(sendButton))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 19, Short.MAX_VALUE)
                .add(bottomSeparator, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 10, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(backButton)
                    .add(disconnectButton)
                    .add(startButton)
                    .add(readyToggleButton))
                .addContainerGap())
        );
        pack();
    }// </editor-fold>//GEN-END:initComponents

	private void sendButtonActionPerformed(ActionEvent evt) {
		if( ! this.messageTextField.getText().trim().equals( "" ) ) {
			try {
				this.view.getController().sendChatMessage( this.messageTextField.getText() );
			} catch (CommunicationException e) {
				JOptionPane.showMessageDialog( this, "Could not send message: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
			}
		}
	}
    
    private void disconnectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disconnectButtonActionPerformed
    	
    	try {
			this.view.getController().disconnect();
		} catch (CommunicationException e) {
			JOptionPane.showMessageDialog( this, "Could not disconnect: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
		}
    }//GEN-LAST:event_disconnectButtonActionPerformed
    
    
    private void readyButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disconnectButtonActionPerformed

    	try {
			this.view.getController().toggleReadinessState();
				    	
	    	this.isReady = ! this.isReady;
	    	
	    	if( this.isReady ) {
	    		this.readyToggleButton.setText( "Not Ready" );
	    	} else {
	    		this.readyToggleButton.setText( "Ready" );
	    	}
		} catch (CommunicationException e) {
			JOptionPane.showMessageDialog( this, "Could change readiness state: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
		}
    }
    
    
    private void kickButtonActionPerformed(java.awt.event.ActionEvent evt) {

    	new Thread( new KickThread( this, this.selectedPlayer ) ).start();
    }
    

	private void startGameActionPerformed(ActionEvent evt) {
		
		try {
			this.view.getController().initGame();
		} catch (CommunicationException e) {
			JOptionPane.showMessageDialog( this, "Could not start game: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
		} catch (ServerPhaseException e) {
			JOptionPane.showMessageDialog( this, "Could not start game: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
		}
	}

	private void playerTableValueChanged(ListSelectionEvent evt) {
		
		if( this.playersTable.getSelectedRow() != -1 && this.isServer() ) {
			this.selectedPlayer = this.rowClientMapping.get( this.playersTable.getSelectedRow() );
			this.kickButton.setEnabled( true );
		} else {
			this.selectedPlayer = null;			
			this.kickButton.setEnabled( false );
		}
	}
    
    private void changeMapButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeMapButtonActionPerformed
    	
    	if( this.mapList.getSelectedValue() != null ) {
    		try {
    			this.view.getController().changeMap( (String) this.mapList.getSelectedValue() );
    		} catch (CommunicationException e) {
				JOptionPane.showMessageDialog( this, "Could not change map: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
    		}
    	}
    }//GEN-LAST:event_changeMapButtonActionPerformed

    private void backButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backButtonActionPerformed
    	this.view.showMainWindow();
        this.dispose();
    }//GEN-LAST:event_backButtonActionPerformed

    private void mapListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_mapListValueChanged
        
        if ( this.mapList.getSelectedIndex() > -1 && this.isServer() ) {
            this.changeMapButton.setEnabled( true );
        } else {
            this.changeMapButton.setEnabled( false );
        }
    }//GEN-LAST:event_mapListValueChanged

    public void clientIsOffline( String clientId ) {
        SCRClientServer cs = new SCRClientServer( this.view, "Error: server connection lost." );
        cs.setVisible( true );
        this.dispose();
    }
    
    private ListModel getMapsListModel() {
        
        DefaultListModel model = new DefaultListModel();
        for ( String s : this.mapNames ) {
            model.addElement( s );
        }

        return model;
    }

    private void loadMaps() {

        List<File> l = new LinkedList<File>();        
        File dir = new File( MAPS_ROOT );
                
        for ( File f : dir.listFiles() ) {
            if ( f.isFile() && !f.isHidden() && f.getName().toLowerCase().endsWith( MAP_FILE_EXTENSION )) {
                l.add(f);
            }
        }        
        
        this.mapFiles = l.toArray( new File[]{} );
        this.mapNames = new String[ this.mapFiles.length ];
        for ( int i = 0; i < this.mapNames.length; i++ ) {
            this.mapNames[i] = this.mapFiles[i].getName().substring(0, this.mapFiles[i].getName().lastIndexOf( MAP_FILE_EXTENSION ));
        }        
    }
    
    private boolean isServer() {
        return this.server;
    }

	public void notifyChatMessage(String alias, String chatMessage) {
		
		String text = this.chatTextArea.getText();
		this.chatTextArea.setText( text + "<" + alias + "> " + chatMessage + "\n" );
	}

	public void notifyWhosHereMessage(Map<String, String> aliasMap, Set<String> readyClientsSet) {
		this.aliasMap = aliasMap;
		this.readyClientsSet = readyClientsSet;

		this.updateClientsList();
	}

	private void updateClientsList() {

		DefaultTableModel tableModel = new DefaultTableModel( new Object[]{"Players", "Status"}, 0 ); 
		
		int row = 0;
		for( String clientId : aliasMap.keySet() ) {
			String alias = this.aliasMap.get( clientId );
			String ready = this.readyClientsSet.contains( clientId ) ? "Ready" : "Not ready";
			
			tableModel.addRow( new Object[]{ alias, ready } );
			
			this.rowClientMapping.put( row, clientId );
			row++;
		}
		
		this.playersTable.setModel( tableModel );
		
		if( view.getController().mayStartGame() && view.getController().isServer() ) {
			this.startButton.setEnabled( true );
		} else {
			this.startButton.setEnabled( false );
		}
	}

	public void notifyMapChanged(String mapName) {
		
		this.mapLabel.setText( "Map: " + mapName );
	}

	public void notifyServerName(String serverName) {

		this.serverNameLabel.setText( "Server name: " + serverName );
	}

	private class KickThread implements Runnable {
		private SCRLobby lobby;
		private String player;
		
		public KickThread( SCRLobby lobby, String player ) {
			this.lobby = lobby;
			this.player = player;
		}
		
		public void run() {
	    	try {
				view.getController().kickPlayer( player );
			} catch (CommunicationException e) {
				JOptionPane.showMessageDialog( lobby, "Could not kick player: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
			}
		}		
	}

	public void notifyClientDisconnected(String clientId) {

		this.aliasMap.remove( clientId );
		this.readyClientsSet.remove( clientId );
		
		this.updateClientsList();
	}
}
